package com.xci.platform.web.aspect;

import com.xci.platform.exception.ValidatorException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.executable.ExecutableValidator;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Set;

/**
 * 参数校验拦截器
 */
@Slf4j
@Aspect
@Order(2)
@Component
public class ValidatorAspect {

    @Resource
    private Validator beanValidator;

    @Resource
    private ExecutableValidator methodValidator;

    @Pointcut("execution(* com.xci.platform..*.service..*.*(..)) && @annotation(org.springframework.validation.annotation.Validated)")
    public void validatorPointcut() {
    }

    @Before("validatorPointcut()")
    public void doBefore(JoinPoint point) {
        Object target = point.getThis();// 获得切入目标对象
        Object[] args = point.getArgs();// 获得切入方法参数

        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();// 获得切入的方法
        // Validated validated = method.getAnnotation(Validated.class);
        // if (validated == null) { //方法上没有验证注解,则直接退出
        //     return;
        // }
        HashMap<String, String> validatorMap = new HashMap<>();

        // 校验以基本数据类型 为方法参数的
        Set<ConstraintViolation<Object>> validResult = validMethodParams(target, method, args);
        for (ConstraintViolation<Object> aValidResult : validResult) {
            setValidatorMap(validatorMap,aValidResult.getPropertyPath().toString(), aValidResult.getMessage());
        }

        // 校验以java bean对象 为方法参数的
        for (Object bean : args) {
            if (null != bean) {
                validResult = validBeanParams(bean);
                for (ConstraintViolation<Object> aValidResult : validResult) {
                    setValidatorMap(validatorMap,aValidResult.getPropertyPath().toString(), aValidResult.getMessage());
                }
            }
        }

        if (validatorMap.size() > 0) {
            throw new ValidatorException(validatorMap);
        }
    }

    private void setValidatorMap(HashMap<String,String> validatorMap, String field, String msg) {
        if (validatorMap.containsKey(field)) {
            validatorMap.put(field, validatorMap.get(field) + ";" + msg);
        }
        else {
            validatorMap.put(field, msg);
        }
    }

    private <T> Set<ConstraintViolation<T>> validMethodParams(T obj, Method method, Object[] params) {
        return methodValidator.validateParameters(obj, method, params);
    }

    private <T> Set<ConstraintViolation<T>> validBeanParams(T bean) {
        return beanValidator.validate(bean);
    }
}
